]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
Rework StDrawingArea not to use ClutterCairoTexture
authorOwen W. Taylor <otaylor@fishsoup.net>
Wed, 3 Mar 2010 23:00:05 +0000 (18:00 -0500)
committerOwen W. Taylor <otaylor@fishsoup.net>
Thu, 11 Mar 2010 20:08:48 +0000 (15:08 -0500)
Having StDrawingArea use ClutterCairoTexture causes circularity
problems with sizing - StDrawingArea wants to use its allocation for
the size of the texture, but ClutterTexture wants to use the size of
the texture to determine the requited size.

Avoid this by making StDrawingArea directly use Cairo and CoglTexture;
while doing this, the API is changed a bit for simplicity and to
match our use case:

 - Instead of clutter_cairo_texture_create(), we have
   st_drawing_area_get_context() to retrieve an already created
   context. This can only be called in the ::repaint signal.

 - The ::redraw signal is changed to ::repaint so we can have
   st_drawing_area_queue_repaint() that doesn't collide with
   clutter_actor_queue_redraw()

 - ::repaint is now emitted lazily when painting the actor rather
   than synchronously at various different points.

https://bugzilla.gnome.org/show_bug.cgi?id=611750

js/ui/altTab.js
js/ui/appDisplay.js
js/ui/widget.js
src/shell-drawing.c
src/shell-drawing.h
src/st/st-drawing-area.c
src/st/st-drawing-area.h

index 46e723dc34ce87f0fe2ed9f0246d58dc337ec43a..865e1f0eb45a01c6696079859a1c40aae61e4f5f 100644 (file)
@@ -695,9 +695,9 @@ AppSwitcher.prototype = {
 
         let n = this._arrows.length;
         let arrow = new St.DrawingArea();
-        arrow.connect('redraw', Lang.bind(this,
-            function (area, texture) {
-                Shell.draw_box_pointer(texture, Shell.PointerDirection.DOWN,
+        arrow.connect('repaint', Lang.bind(this,
+            function (area) {
+                Shell.draw_box_pointer(area, Shell.PointerDirection.DOWN,
                                        TRANSPARENT_COLOR,
                                        this._curApp == n ? POPUP_ARROW_COLOR : POPUP_UNFOCUSED_ARROW_COLOR);
             }));
index 7ae0b08f08655e1734fe05944d2d98882e8c9513..fb41bb4ad7d33369964f2a3d06d82b0d4fb51c0f 100644 (file)
@@ -633,8 +633,8 @@ AppIconMenu.prototype = {
         this._windowContainerBox.connect('style-changed', Lang.bind(this, this._onStyleChanged));
 
         this._arrow = new St.DrawingArea();
-        this._arrow.connect('redraw', Lang.bind(this, function (area, texture) {
-            Shell.draw_box_pointer(texture,
+        this._arrow.connect('repaint', Lang.bind(this, function (area) {
+            Shell.draw_box_pointer(area,
                                    Shell.PointerDirection.LEFT,
                                    this._borderColor,
                                    this._backgroundColor);
@@ -884,7 +884,7 @@ AppIconMenu.prototype = {
         if (themeNode.get_border_color(St.Side.LEFT, color)) {
             this._borderColor = color;
         }
-        this._arrow.emit_redraw();
+        this._arrow.queue_repaint();
     }
 };
 Signals.addSignalMethods(AppIconMenu.prototype);
index b7e8ca3359ed904c1ce0a8106947e3ae0d98d5b4..d6fe8fe6d2a398d903d10bba6a2dd09566597382 100644 (file)
@@ -9,6 +9,7 @@ const Lang = imports.lang;
 const Pango = imports.gi.Pango;
 const Shell = imports.gi.Shell;
 const Signals = imports.signals;
+const St = imports.gi.St;
 const Gettext = imports.gettext.domain('gnome-shell');
 const _ = Gettext.gettext;
 
@@ -117,10 +118,9 @@ ClockWidget.prototype = {
                                         // it's the same in both modes
                                         height: COLLAPSED_WIDTH });
 
-        this.collapsedActor = new Clutter.CairoTexture({ width: COLLAPSED_WIDTH,
-                                                         height: COLLAPSED_WIDTH,
-                                                         surface_width: COLLAPSED_WIDTH,
-                                                         surface_height: COLLAPSED_WIDTH });
+        this.collapsedActor = new St.DrawingArea({ width: COLLAPSED_WIDTH,
+                                                   height: COLLAPSED_WIDTH });
+        this.collapsedActor.connect('repaint', Lang.bind(this, this._repaintClock));
 
         this._update();
     },
@@ -139,18 +139,18 @@ ClockWidget.prototype = {
     },
 
     _update: function() {
-        let time = new Date();
-        let msec_remaining = 60000 - (1000 * time.getSeconds() +
-                                      time.getMilliseconds());
+        this.currentTime = new Date();
+        let msec_remaining = 60000 - (1000 * this.currentTime.getSeconds() +
+                                      this.currentTime.getMilliseconds());
         if (msec_remaining < 500) {
-            time.setMinutes(time.getMinutes() + 1);
+            this.currentTime.setMinutes(time.getMinutes() + 1);
             msec_remaining += 60000;
         }
 
         if (this.state == STATE_COLLAPSED || this.state == STATE_COLLAPSING)
-            this._updateCairo(time);
+            this.collapsedActor.queue_repaint();
         else
-            this._updateText(time);
+            this._updateText();
 
         if (this.timer)
             Mainloop.source_remove(this.timer);
@@ -160,13 +160,13 @@ ClockWidget.prototype = {
 
     _updateText: function(time) {
         // Translators: This is a time format.
-        this.actor.set_text(time.toLocaleFormat(_("%H:%M")));
+        this.actor.set_text(this.currentTime.toLocaleFormat(_("%H:%M")));
     },
 
-    _updateCairo: function(time) {
-        Shell.draw_clock(this.collapsedActor,
-                         time.getHours() % 12,
-                         time.getMinutes());
+    _repaintClock: function(area) {
+        Shell.draw_clock(area,
+                         this.currentTime.getHours() % 12,
+                         this.currentTime.getMinutes());
     }
 };
 
index c0e7632c2d6ea47d71508f4d8283673ec6dfa882..01055c99ba4e7172be328a6ea640fe868347ffe4 100644 (file)
@@ -6,7 +6,7 @@
 #include <math.h>
 
 void
-shell_draw_clock (ClutterCairoTexture *texture,
+shell_draw_clock (StDrawingArea       *area,
                   int                  hour,
                   int                  minute)
 {
@@ -15,15 +15,14 @@ shell_draw_clock (ClutterCairoTexture *texture,
   double xc, yc, radius, hour_radius, minute_radius;
   double angle;
 
-  clutter_cairo_texture_get_surface_size (texture, &width, &height);
+  st_drawing_area_get_surface_size (area, &width, &height);
   xc = (double)width / 2;
   yc = (double)height / 2;
   radius = (double)(MIN(width, height)) / 2 - 2;
   minute_radius = radius - 3;
   hour_radius = radius / 2;
 
-  clutter_cairo_texture_clear (texture);
-  cr = clutter_cairo_texture_create (texture);
+  cr = st_drawing_area_get_context (area);
   cairo_set_line_width (cr, 1.0);
 
   /* Outline */
@@ -48,8 +47,6 @@ shell_draw_clock (ClutterCairoTexture *texture,
                  xc + minute_radius * cos (angle),
                  yc + minute_radius * sin (angle));
   cairo_stroke (cr);
-
-  cairo_destroy (cr);
 }
 
 /**
@@ -117,7 +114,7 @@ shell_fade_app_icon (ClutterTexture *source)
 }
 
 void
-shell_draw_box_pointer (ClutterCairoTexture   *texture,
+shell_draw_box_pointer (StDrawingArea         *area,
                         ShellPointerDirection  direction,
                         ClutterColor          *border_color,
                         ClutterColor          *background_color)
@@ -125,10 +122,9 @@ shell_draw_box_pointer (ClutterCairoTexture   *texture,
   guint width, height;
   cairo_t *cr;
 
-  clutter_cairo_texture_get_surface_size (texture, &width, &height);
+  st_drawing_area_get_surface_size (area, &width, &height);
 
-  clutter_cairo_texture_clear (texture);
-  cr = clutter_cairo_texture_create (texture);
+  cr = st_drawing_area_get_context (area);
 
   cairo_set_line_width (cr, 1.0);
 
@@ -169,8 +165,6 @@ shell_draw_box_pointer (ClutterCairoTexture   *texture,
   clutter_cairo_set_source_color (cr, background_color);
 
   cairo_fill (cr);
-
-  cairo_destroy (cr);
 }
 
 static void
index f60adf41af99ab54681e950971f02dfe8d345fda..ac6edf90ab6aa739ad2bdc69488542e33d9dcec7 100644 (file)
@@ -4,6 +4,7 @@
 #define __SHELL_DRAWING_H__
 
 #include <clutter/clutter.h>
+#include "st.h"
 
 G_BEGIN_DECLS
 
@@ -14,12 +15,12 @@ typedef enum {
   SHELL_POINTER_RIGHT
 } ShellPointerDirection;
 
-void shell_draw_box_pointer (ClutterCairoTexture   *texture,
+void shell_draw_box_pointer (StDrawingArea         *area,
                              ShellPointerDirection  direction,
                              ClutterColor          *border_color,
                              ClutterColor          *background_color);
 
-void shell_draw_clock (ClutterCairoTexture *texture,
+void shell_draw_clock (StDrawingArea       *area,
                       int                  hour,
                       int                  minute);
 
index 4391ac13eb22c0e7b6b38fe5738795280368702c..720c4003da512b719557317cb5f3724315b3cb87 100644 (file)
  *
  * #StDrawingArea is similar to #ClutterCairoTexture in that
  * it allows drawing via Cairo; the primary difference is that
- * it is dynamically sized.  To use, connect to the #StDrawingArea::redraw
+ * it is dynamically sized.  To use, connect to the #StDrawingArea::repaint
  * signal, and inside the signal handler, call
- * clutter_cairo_texture_create() to begin drawing.  The
- * #StDrawingArea::redraw signal will be emitted by default when the area is
+ * st_drawing_area_get_context() to get the Cairo context to draw to.  The
+ * #StDrawingArea::repaint signal will be emitted by default when the area is
  * resized or the CSS style changes; you can use the
- * st_drawing_area_emit_redraw() as well.
+ * st_drawing_area_queue_repaint() as well.
  */
 
 #include "st-drawing-area.h"
 
 #include <cairo.h>
 
-G_DEFINE_TYPE(StDrawingArea, st_drawing_area, ST_TYPE_BIN);
+/* Cairo stores the data in native byte order as ARGB but Cogl's pixel
+   formats specify the actual byte order. Therefore we need to use a
+   different format depending on the architecture */
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define PIXEL_FORMAT COGL_PIXEL_FORMAT_BGRA_8888_PRE
+#else
+#define PIXEL_FORMAT COGL_PIXEL_FORMAT_ARGB_8888_PRE
+#endif
+
+G_DEFINE_TYPE(StDrawingArea, st_drawing_area, ST_TYPE_WIDGET);
 
 struct _StDrawingAreaPrivate {
-  ClutterCairoTexture *texture;
+  CoglHandle *texture;
+  CoglHandle *material;
+  cairo_t *context;
+  guint needs_repaint : 1;
+  guint in_repaint : 1;
 };
 
 /* Signals */
 enum
 {
-  REDRAW,
+  REPAINT,
   LAST_SIGNAL
 };
 
 static guint st_drawing_area_signals [LAST_SIGNAL] = { 0 };
 
 static void
-st_drawing_area_allocate (ClutterActor          *self,
-                          const ClutterActorBox *box,
-                          ClutterAllocationFlags flags)
+st_drawing_area_dispose (GObject *object)
+{
+  StDrawingArea *area = ST_DRAWING_AREA (object);
+  StDrawingAreaPrivate *priv = area->priv;
+
+  if (priv->material != COGL_INVALID_HANDLE)
+    {
+      cogl_handle_unref (priv->material);
+      priv->material = COGL_INVALID_HANDLE;
+    }
+
+  if (priv->texture != COGL_INVALID_HANDLE)
+    {
+      cogl_handle_unref (priv->texture);
+      priv->texture = COGL_INVALID_HANDLE;
+    }
+}
+
+static void
+st_drawing_area_paint (ClutterActor *self)
 {
-  StThemeNode *theme_node;
-  ClutterActorBox content_box;
   StDrawingArea *area = ST_DRAWING_AREA (self);
-  int width = box->x2 - box->x1;
-  int height = box->y2 - box->y1;
+  StDrawingAreaPrivate *priv = area->priv;
+  StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
+  ClutterActorBox allocation_box;
+  ClutterActorBox content_box;
+  int width, height;
+  CoglColor color;
+  guint8 paint_opacity;
+
+  (CLUTTER_ACTOR_CLASS (st_drawing_area_parent_class))->paint (self);
+
+  clutter_actor_get_allocation_box (self, &allocation_box);
+  st_theme_node_get_content_box (theme_node, &allocation_box, &content_box);
 
-  (CLUTTER_ACTOR_CLASS (st_drawing_area_parent_class))->allocate (self, box, flags);
+  width = (int)(0.5 + content_box.x2 - content_box.x1);
+  height = (int)(0.5 + content_box.y2 - content_box.y1);
 
-  theme_node = st_widget_get_theme_node (ST_WIDGET (self));
+  if (priv->material == COGL_INVALID_HANDLE)
+    priv->material = cogl_material_new ();
 
-  st_theme_node_get_content_box (theme_node, box, &content_box);
+  if (priv->texture != COGL_INVALID_HANDLE &&
+      (width != cogl_texture_get_width (priv->texture) ||
+       height != cogl_texture_get_height (priv->texture)))
+    {
+      cogl_handle_unref (priv->texture);
+      priv->texture = COGL_INVALID_HANDLE;
+    }
 
   if (width > 0 && height > 0)
     {
-      clutter_cairo_texture_set_surface_size (area->priv->texture,
-                                              content_box.x2 - content_box.x1,
-                                              content_box.y2 - content_box.y1);
-      g_signal_emit (G_OBJECT (self), st_drawing_area_signals[REDRAW], 0,
-                     area->priv->texture);
+      if (priv->texture == COGL_INVALID_HANDLE)
+        {
+          priv->texture = cogl_texture_new_with_size (width, height,
+                                                      COGL_TEXTURE_NONE,
+                                                      PIXEL_FORMAT);
+          priv->needs_repaint = TRUE;
+        }
+
+      if (priv->needs_repaint)
+        {
+          cairo_surface_t *surface;
+
+          surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+          priv->context = cairo_create (surface);
+          priv->in_repaint = TRUE;
+
+          g_signal_emit ((GObject*)area, st_drawing_area_signals[REPAINT], 0);
+
+          priv->in_repaint = FALSE;
+          cairo_destroy (priv->context);
+          priv->context = NULL;
+
+          cogl_texture_set_region (priv->texture, 0, 0, 0, 0, width, height, width, height,
+                                   PIXEL_FORMAT,
+                                   cairo_image_surface_get_stride (surface),
+                                   cairo_image_surface_get_data (surface));
+
+          cairo_surface_destroy (surface);
+        }
+    }
+
+  cogl_material_set_layer (priv->material, 0, priv->texture);
+
+  if (priv->texture)
+    {
+      paint_opacity = clutter_actor_get_paint_opacity (self);
+      cogl_color_set_from_4ub (&color,
+                               paint_opacity, paint_opacity, paint_opacity, paint_opacity);
+      cogl_material_set_color (priv->material, &color);
+
+      cogl_set_source (priv->material);
+      cogl_rectangle_with_texture_coords (content_box.x1, content_box.y1,
+                                          width, height,
+                                          0.0f, 0.0f, 1.0f, 1.0f);
     }
 }
 
 static void
 st_drawing_area_style_changed (StWidget  *self)
 {
+  StDrawingArea *area = ST_DRAWING_AREA (self);
+  StDrawingAreaPrivate *priv = area->priv;
+
   (ST_WIDGET_CLASS (st_drawing_area_parent_class))->style_changed (self);
 
-  st_drawing_area_emit_redraw (ST_DRAWING_AREA (self));
+  priv->needs_repaint = TRUE;
 }
 
 static void
@@ -75,17 +163,18 @@ st_drawing_area_class_init (StDrawingAreaClass *klass)
   ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
   StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
 
-  actor_class->allocate = st_drawing_area_allocate;
+  gobject_class->dispose = st_drawing_area_dispose;
+  actor_class->paint = st_drawing_area_paint;
   widget_class->style_changed = st_drawing_area_style_changed;
 
-  st_drawing_area_signals[REDRAW] =
-    g_signal_new ("redraw",
+  st_drawing_area_signals[REPAINT] =
+    g_signal_new ("repaint",
                   G_TYPE_FROM_CLASS (klass),
                   G_SIGNAL_RUN_LAST,
-                  G_STRUCT_OFFSET (StDrawingAreaClass, redraw),
+                  G_STRUCT_OFFSET (StDrawingAreaClass, repaint),
                   NULL, NULL,
                   g_cclosure_marshal_VOID__OBJECT,
-                  G_TYPE_NONE, 1, CLUTTER_TYPE_CAIRO_TEXTURE);
+                  G_TYPE_NONE, 0);
 
   g_type_class_add_private (gobject_class, sizeof (StDrawingAreaPrivate));
 }
@@ -95,31 +184,74 @@ st_drawing_area_init (StDrawingArea *area)
 {
   area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area, ST_TYPE_DRAWING_AREA,
                                             StDrawingAreaPrivate);
-  area->priv->texture = CLUTTER_CAIRO_TEXTURE (clutter_cairo_texture_new (1, 1));
-  clutter_container_add_actor (CLUTTER_CONTAINER (area), CLUTTER_ACTOR (area->priv->texture));
+  area->priv->texture = COGL_INVALID_HANDLE;
+}
+
+/**
+ * st_drawing_area_queue_repaint:
+ * @area: the #StDrawingArea
+ *
+ * Will cause the actor to emit a ::repaint signal before it is next
+ * drawn to the scene. Useful if some parameters for the area being
+ * drawn other than the size or style have changed. Note that
+ * clutter_actor_queue_redraw() will simply result in the same
+ * contents being drawn to the scene again.
+ */
+void
+st_drawing_area_queue_repaint (StDrawingArea *area)
+{
+  StDrawingAreaPrivate *priv;
+
+  g_return_if_fail (ST_IS_DRAWING_AREA (area));
+
+  priv = area->priv;
+
+  priv->needs_repaint = TRUE;
+  clutter_actor_queue_redraw (CLUTTER_ACTOR (area));
 }
 
 /**
- * st_drawing_area_get_texture:
+ * st_drawing_area_get_context:
+ * @area: the #StDrawingArea
  *
- * Return Value: (transfer none):
+ * Gets the Cairo context to paint to. This function must only be called
+ * from a signal hander for the ::repaint signal.
+ *
+ * Return Value: (transfer none): the Cairo context for the paint operation
  */
-ClutterCairoTexture *
-st_drawing_area_get_texture (StDrawingArea *area)
+cairo_t *
+st_drawing_area_get_context (StDrawingArea *area)
 {
-  return area->priv->texture;
+  g_return_val_if_fail (ST_IS_DRAWING_AREA (area), NULL);
+  g_return_val_if_fail (area->priv->in_repaint, NULL);
+
+  return area->priv->context;
 }
 
 /**
- * st_drawing_area_emit_redraw:
- * @area: A #StDrawingArea
+ * st_drawing_area_get_surface_size:
+ * @area: the #StDrawingArea
+ * @width: (out): location to store the width of the painted area
+ * @height: (out): location to store the height of the painted area
  *
- * Immediately emit a redraw signal.  Useful if
- * some parameters for the area being drawn other
- * than the size or style have changed.
+ * Gets the size of the cairo surface being painted to, which is equal
+ * to the size of the content area of the widget. This function must
+ * only be called from a signal hander for the ::repaint signal.
  */
 void
-st_drawing_area_emit_redraw (StDrawingArea *area)
+st_drawing_area_get_surface_size (StDrawingArea *area,
+                                  guint         *width,
+                                  guint         *height)
 {
-  g_signal_emit ((GObject*)area, st_drawing_area_signals[REDRAW], 0, area->priv->texture);
+  StDrawingAreaPrivate *priv;
+
+  g_return_if_fail (ST_IS_DRAWING_AREA (area));
+  g_return_if_fail (area->priv->in_repaint);
+
+  priv = area->priv;
+
+  if (width)
+    *width = cogl_texture_get_width (priv->texture);
+  if (height)
+    *height = cogl_texture_get_height (priv->texture);
 }
index 36083729e479d2819b55ce6d2f26996fa11655af..429e749b0985867534644a1066a04e1a5cef0685 100644 (file)
@@ -2,7 +2,8 @@
 #ifndef __ST_DRAWING_AREA_H__
 #define __ST_DRAWING_AREA_H__
 
-#include "st-bin.h"
+#include "st-widget.h"
+#include <cairo.h>
 
 #define ST_TYPE_DRAWING_AREA                 (st_drawing_area_get_type ())
 #define ST_DRAWING_AREA(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_DRAWING_AREA, StDrawingArea))
@@ -18,22 +19,24 @@ typedef struct _StDrawingAreaPrivate StDrawingAreaPrivate;
 
 struct _StDrawingArea
 {
-    StBin parent;
+    StWidget parent;
 
     StDrawingAreaPrivate *priv;
 };
 
 struct _StDrawingAreaClass
 {
-    StBinClass parent_class;
+    StWidgetClass parent_class;
 
-    void (*redraw) (StDrawingArea *area, ClutterCairoTexture *texture);
+    void (*repaint) (StDrawingArea *area);
 };
 
 GType st_drawing_area_get_type (void) G_GNUC_CONST;
 
-ClutterCairoTexture *st_drawing_area_get_texture (StDrawingArea *area);
-
-void st_drawing_area_emit_redraw (StDrawingArea *area);
+void     st_drawing_area_queue_repaint    (StDrawingArea *area);
+cairo_t *st_drawing_area_get_context      (StDrawingArea *area);
+void     st_drawing_area_get_surface_size (StDrawingArea *area,
+                                           guint         *width,
+                                           guint         *height);
 
 #endif /* __ST_DRAWING_AREA_H__ */